Acessando e visualizando dados da câmara dos deputados brasileiros

O objetivo desse guia é dar noções iniciais de como utilizar a base de dados da câmara dos deputados e alguns princípios básicos de visualização de dados.

Base de dados da camâra dos deputados

A base que trabalharemos a seguir contém dados obtidos pelos orgãos oficiais da camâra dos deputados em Brasília, através da lei da transparência.

A organização e estruturação dos dados foi feita pelos alunos da Unicamp Alan Godoy, Eliezer Silva e Saullo Oliveira, na ocasião do hackathon organizado pela camâra em 2013 do qual fizeram parte.

A base é vasta e contém diversas informações de mandatos de deputados, votações, gastos, proposições de leis, discursos, entre outros. A descrição do conteúdo completo da base pode ser encontrada em https://docs.google.com/document/d/1WJmwBvlJFzqVNuiMT0OIdlnuuNY73KSDyIdGlAltUY4/

Este tutorial usa um recorte da base, denominado "camara filtrado".

Conectando-se e extraindo informação da base

Conexão com o servidor

A base de dados está hospedada em um servidor mariadb (antigo MySQL). É possível acessa-lo através de uma interface python, utilizando a biblioteca MySQLdb.

Também utilizaremos as bibliotecas numpy e pandas para criar estruturas de dados para armazenar e manipular as informações da base


In [ ]:
#importacao de toda a biblioteca necessaria
import MySQLdb
import numpy as np
import pandas as pd

#conectando com o servidor:
db = MySQLdb.connect(host="localhost", user="hackday", passwd="hackdaydadosabertos", db="camarafiltrado")
cur = db.cursor() #cur eh o cursor por onde as chamadas ao banco sao feitas

Consultas na base de dados

A linguagem MySQL possui uma interface para se fazer consultas na base de dados, afim de extrair informação relevante. Essas consultas são chamadas de query.

Uma query pode selecionar uma visualização específica do banco de dados. Típicamente ela é composta dos seguintes campos:

SELECT <lista de elementos requeridos> FROM <tabela ou selecao que possui os elementos requeridos> WHERE <condicoes dos elementos>;

A seguir, exemplo de query que retorna o nome e estado de deputados do PT, sem ensino superior completo:


In [ ]:
query = '''SELECT nome_parlamentar, uf FROM deputados WHERE eleicao_partido = "PT" and eleicao_grau_instrucao < 8;''' #string com query
cur.execute(query) #executa query
rows = cur.fetchall()

Para ler o resultado da query, utiliza-se a função fetchall.

A biblioteca MySQLdb retorna cada linha do resultado como uma tupla. Para facilitar o resultado, vamos primeiro transformar as tuplas em lista e em seguida transforma-la em um dataframe da biblioteca pandas.


In [ ]:
lrows = [list(tupl) for tupl in rows] # Transformando tuplas em lista
dados = pd.DataFrame(lrows, columns=["Nome Parlamentar", "estado"]) # Convertendo para dataframe
dados

Cruzando tabelas

É comum acontecer de cruzarmos informações presentes em mais de uma tabela ou seleção.

É possível aninhar queries, de forma que uma query usa como fonte de dados o resultado de outra tabela.

Exemplo: Selecionar nomes e partidos de deputados que votaram a favor da votação #1089


In [ ]:
query = '''
SELECT nome_parlamentar, eleicao_partido 
FROM deputados 
WHERE id_deputado in (SELECT id_deputado from votos where id_votacao=1089 and voto=0);
'''

cur.execute(query) 
rows = cur.fetchall()
lrows = [list(tupl) for tupl in rows]
dados = pd.DataFrame(lrows, columns=["Nome Parlamentar", "estado"])
dados

Outro comando útil é o INNER JOIN, que consegue unir duas tabelas, com algum criterio.

Sintaxe: <tabela_a> INNER JOIN <tabela_b> ON <criterio de uniao>

Para se aprofundar mais na linguagem, consulte: http://dev.mysql.com/doc/

Visualizações e primeiras intuições

Vamos fazer agora uma querie um pouco mais complexa, que correlaciona a idade de deputados, com os gastos realizados na campanha eleitoral.

A biblioteca pandas possui a função plot, que gera gráficos que auxiliam a análise de resultados.


In [ ]:
query='''
select a.id_deputado, a.eleicao_partido,  b.gastos_campanha, DATE_FORMAT(FROM_DAYS(DATEDIFF('2014-05-08',a.data_nascimento)), '%Y')+0 as idade 
from 
	deputados as a
	inner join (
		select id_deputado, cast(sum(valor) as signed) as gastos_campanha 
		from deputados_eleicoes_despesas 
		group by id_deputado) as b 
	on a.id_deputado = b.id_deputado
;
'''


cur.execute(query) 
rows = cur.fetchall()

lrows = [list(tupl) for tupl in rows]

df = pd.DataFrame(lrows, columns=["id", "partido", "gastos_campanha", "idade"])

df.plot(x='gastos_campanha', y='idade', style='o')
df

É possível observar propriedades interessantes no gráfico acima? Existe alguma relação entre idade e gasto de campanha? Como explicar isso?

Tarefa prática

Crie uma nova query e faça uma nova visualização interessante. Tente mostra-la graficamente também.

Sugestões: partido X gastos; gastos de campanha X gastos durante o mandato; idade X assiduidade em reuniões


In [ ]:
#insira seu código aqui

Visualizando texto

Além de dados númericos, é comum a presença de texto nos dados a serem analisados. Uma forma interessante de visualizar e analisar grandes conjuntos de texto, é utlizando nuvens de palavras (wordclouds).

A seguir demonstraremos um exemplo de criação de wordclouds que resumem o conteúdo de proposições de leis escritas por deputados, por estado brasileiro.

Utilizaremos a biblioteca python wordcloud


In [ ]:
import wordcloud
import matplotlib.image as mpimg
from unicodedata import normalize 

cur = db.cursor()

#wordclouds por estado

#seleciona propostas de lei (tipo PL), escritas por deputados (TipoParlamentar_10000), no ano de 2013   
cur.execute("select id_proposicao from proposicoes where ano=2013 and tipo='PL' and autor1 in(select id_autor from autores_proposicoes where tipo='TipoParlamentar_10000');")
rows = cur.fetchall()
proposicoes = [element for tupl in rows for element in tupl]

print "numero de proposicoes de deputados:", len(proposicoes)

In [ ]:
#dicionário que armazenará coleção de textos para cada UF
uf2tex = {}

#para cada proposta, pega autor e uf
for proposicao in proposicoes:
    #pega texto da proposta
    cur.execute("select inteiro_teor from proposicoes where id_proposicao=(%s) and inteiro_teor is not null", (proposicao,))
    pretextocod = cur.fetchone()
    try:
        textocod = str(pretextocod[0])
    except:
        continue
    #tira acentos do texto, substituindo-os por letras comuns (ex: é -> e, ç -> c)
    texto = normalize('NFKD', textocod.decode("latin1")).encode('ASCII','ignore').lower()
    
    cur.execute("select uf from deputados where id_deputado in (select autor1 from proposicoes where id_proposicao=(%s))", (proposicao,))
    uf = cur.fetchone()[0] #pega o uf do deputado
    
    #para cada UF, junta todas as propostas
    if texto:
        #texto = unicode(texto,encoding='latin1')
        if uf not in uf2tex.keys():
            uf2tex[uf] = texto
        else:
            uf2tex[uf] += texto

A wordcloud analisa a frequencia das palavras dos textos e produz uma imagem destacando as palavras mais frequentes. Entretanto, as palavras mais frequentes geralmente são pouco informativas, como artigos, conjunções, etc.

Para sanar esse problema, elaboramos uma lista de palavras, chamadas stopwords que serão excluidas da wordcloud. Essa lista possui também palavras específicas da base, como "brasil", "deputado", "camara", que também podem ser descartadas.


In [ ]:
stopwords = ["o", "e", "a", "as", "de", "da", "do", "dos", "das", "um", "uma", "umas", "que", "em", "no", "por", "com", "para", "os", "ao", "se", "na", "ou", "como", "pela", "mais", "nos", "ser" ]
stopwords += ["n", "s", "p", "c", "1", "000", "5", "0", "2", "3", "4", "00", "2013", "m", "es", "lei", "art", "rio", "ncia", "comiss", "projeto", "dio", "ria", "d", "brasil", "comissao", "nao", "sim"]
stopwords += ["aos", "sua", "anos", "2012", "pelo", "sobre", "seu", "1o", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "b", "i", "ii", "iii", "f", "2000", "2001", "2002", "2003", "2004"]
stopwords += ["2005", "2006", "2007", "2008", "2009", "2010", "2011", "d", "nas", "250", "dois", "esta", "tem", "sr", "relator", "11", "12", "sobre", "senador", "foi", "senhor", "sem", "trata"]
stopwords += ["040", "sao", "mas", "deputados", "deputado", "camara", "ha", "todos", "mil", "foi", "sem", "05", "caput", "fins", "tema", "quando", "poder", "2o", "paulo", "68", "r", "seis", "tambem"]

Finalmente, a execução das wordclouds é feita.


In [ ]:
#cria uma wordcloud para cada estado
for estado in uf2tex.keys():
    TEXT = uf2tex[estado]
    if len(TEXT) < 5 or not TEXT:
        continue
    
    words = wordcloud.process_text(TEXT, stopwords=stopwords)
    # Computa a posicao das palavras
    elements = wordcloud.fit_words(words)
    # Desenha as palavras no arquivo png
    wordcloud.draw(elements, estado+".png", scale=2)
    img = mpimg.imread(estado+".png")
    plt.figure()
    plt.imshow(img)

Tarefa

Crie uma wordcloud para os textos de proposições de cada partido diferente.


In [ ]:
#insira seu código aqui